//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<object_factory.cpp>
///	@path	~/src/object/
///	@date	2007/06/28
///	@desc	.

#include "config/config.h"
#include "config/os.h"

#include <vector>
#include <map>
#include <set>
#include <algorithm>

#include "lib/utilities/dict.h"
#include "lib/utilities/debug.h"
#include "lib/utilities/observer.h"

#include "lib/system/lock.h"

#include "object/IObject.h"
#include "object/IBuffer.h"

#define	IOBJECT_ALLOCATION_DEBUG		0

namespace xeres {

	// shortcut
	typedef ObjectFactory::ConstructObjectFn ConstructObjectFn;

	// magic number of class entry
#define	CLASS_ENTRY_MAGIC_NUM		(0x1246777a)

	enum ClassType
	{
		CLASS_NONE			= 0 ,	// not a class
		CLASS_TYPE			,		// class is a registered type
		CLASS_INTERFACE		,		// class is an interface
		CLASS_DYNAMIC		,		// class can be dynamically created
		CLASS_STATIC		,		// class only support static creation
		CLASS_last ,
	};

	/// Internal structure for IObject interface/class information registry.
	class ClassEntry
	{
	public:

		// data

		const int					m_magicNum;
		const ClassType				m_classType;
		WString						m_className;
		int							m_classObjects;
		size_t						m_size;
		ClassEntry *				m_ancient;
		ConstructObjectFn			m_constructor;
		std::vector<ClassID>		m_ancientList;
		std::map<const void*,void*>	m_classSlot;

#if XERES_IOBJECT_INSTANCE_TRACE
		std::set<IObject*>			m_traces;
#endif


		/// @ctor
		ClassEntry( ClassType type , ClassEntry * ancient , const WString& name , size_t size , ConstructObjectFn constructor = NULL )
			: m_magicNum( CLASS_ENTRY_MAGIC_NUM )
			, m_classType( type )
			, m_className( name )
			, m_classObjects( 0 )
			, m_size( size )
			, m_ancient( ancient )
			, m_constructor( constructor )
		{
			assert( m_className.size() > 0 );
			if( m_constructor )
			{
				assert( m_size > 0 );
			}
			wchar_t buf[256];
			swprintf( buf , _S("IObjects/class/instance/%s:") , name.c_str() );
			ObserveOnly( buf , m_classObjects );
		}

		// dtor
		~ClassEntry( void )
		{
			if( m_classObjects > 0 )
			{
				TRACE_ERROR( _S("ClassEntry::DTOR: Class '%s' remains objects %i un-released.") ,
					m_className.c_str() , m_classObjects );
			}
#if XERES_IOBJECT_INSTANCE_TRACE
			if( !m_traces.empty() )
			{
				for( std::set<IObject*>::iterator it = m_traces.begin() , end = m_traces.end() ;
					it != end ; ++it )
				{
					TRACE_DEBUG( _S("Unreleased %s : %u") , m_className.c_str() , *it );
				}
			}
#endif
		}

		// Validate class entry.
		void Validate( void ) const
		{
			if( m_magicNum != CLASS_ENTRY_MAGIC_NUM )
			{
				TRACE_ERROR( _S("ClassEntry::validate: Magic number of class is invalid, internal structure corrupted.") );
				FATAL_ABORT( _S("ObjectFactory internal structure corrupted.") );
			}
		}

		// Allocate new space for object instance
		void * Alloc( void )
		{
			assert( m_size > 0 );

			// stat
			atom_inc( &m_classObjects );

#if IOBJECT_ALLOCATION_DEBUG
			TRACE_DEBUG( _S("ClassEntry::allocate: Allocate instance space of class '%s'(%i object(s) remain).") ,
				m_className.c_str() , m_classObjects );
#endif

#if XERES_IOBJECT_INSTANCE_TRACE
			void * ptr = _aligned_malloc( m_size , ALIGN_SIZE );
			m_traces.insert( (IObject*)ptr );
			return ptr;
#else
			// allocate new memory space
			return _aligned_malloc( m_size , ALIGN_SIZE );
#endif
		}

		// Allocate buffer for IBuffer instance.
		void * AllocBuffer( size_t size )
		{
			// stat
			atom_inc( &m_classObjects );

#if IOBJECT_ALLOCATION_DEBUG
			TRACE_DEBUG( _S("ClassEntry::allocate: Allocate IBuffer space of %i byte (%i object(s) remain).") ,
				size , m_classObjects );
#endif

#if XERES_IOBJECT_INSTANCE_TRACE
			void * ptr = _aligned_malloc( size , ALIGN_SIZE );
			m_traces.insert( (IObject*)ptr );
			return ptr;
#else
			// allocate new memory space
			return _aligned_malloc( size , ALIGN_SIZE );
#endif
		}

		// Deallocate object released space.
		void Dealloc( void * object )
		{
			assert( m_size > 0 );

#ifdef _DEBUG
			// wipe out space info
			memset( object , 0 , m_size );
#endif

#if XERES_IOBJECT_INSTANCE_TRACE
			m_traces.erase( (IObject*)object );
#endif
			// deallocate space
			_aligned_free( object );

			// stat
			atom_dec( &m_classObjects );

#if IOBJECT_ALLOCATION_DEBUG
			TRACE_DEBUG( _S("ClassEntry::del: Deallocate instance space of class '%s'(%i object(s) remain).") ,
				m_className.c_str() , m_classObjects );
#endif
		}

		// Deallocate buffer space.
		void DeallocBuffer( void * buffer , size_t size )
		{
			assert( size > 0 );

#ifdef _DEBUG
			// clear space
			memset( buffer , 0 , size );
#endif
			
			// deallocate space
			_aligned_free( buffer );

			// stat
			atom_dec( &m_classObjects );

#if IOBJECT_ALLOCATION_DEBUG
			TRACE_DEBUG( _S("ClassEntry::del: Deallocate IBuffer space of %i byte (%i object(s) remain).") ,
				size , m_classObjects );
#endif
		}

		// Register slot
		bool RegisterSlot( const void * key , void * value )
		{
			if( m_classSlot.find( key ) != m_classSlot.end() )
				return false;
			m_classSlot[ key ] = value;
			return true;
		}

		// Delete slot
		bool DeleteSlot( const void * key )
		{
			std::map<const void*,void*>::iterator it = m_classSlot.find( key );
			if( it == m_classSlot.end() )
				return false;
			m_classSlot.erase( it->second );
			return true;
		}

		// Get slot.
		bool GetSlot( const void * key , void *& value )
		{
			std::map<const void*,void*>::iterator it = m_classSlot.find( key );
			if( it == m_classSlot.end() )
				return false;
			value = it->second;
			return true;
		}

		// Set slot.
		bool SetSlot( const void * key , void * value )
		{
			std::map<const void*,void*>::iterator it = m_classSlot.find( key );
			if( it == m_classSlot.end() )
				return false;
			it->second = value;
			return true;
		}

		// Get slot from inheritance.
		bool GetInheritSlot( const void * key , void *& value )
		{
			ClassEntry * op = this;
			while( op )
			{
				if( op->GetSlot( key , value ) )
					return true;
				op = op->m_ancient;
			}
			return false;
		}
	};

	namespace
	{
		// Object factory implementation.
		class _ObjectFactoryImpl
		{
		private:

			// ctor
			_ObjectFactoryImpl( void )
				: m_IObjectClass( NULL )
				, m_IBufferClass( NULL )
			{
			}

			// dtor
			~_ObjectFactoryImpl( void )
			{
				for( NameMap::iterator it = m_nameMap.begin() ;
					it != m_nameMap.end() ; ++it )
				{
					delete it->second;
				}
			}

			// Validate class id.
			static void ValidateClassID( const ClassID id )
			{
				((ClassEntry*)id)->Validate();
			}
			
			//Build inheritance table.
			void BuildInheritance( ClassEntry * newEntry , const ClassID ancient )
			{
				TRACK_FUNCTION_SCOPE();

				// validate id
				ValidateClassID( ancient );

				// get ClassEntry
				ClassEntry * ancientEntry = (ClassEntry*)ancient;
				newEntry->m_ancientList = ancientEntry->m_ancientList;
				newEntry->m_ancientList.push_back( ancient );

				// sort table
				std::sort( newEntry->m_ancientList.begin() , newEntry->m_ancientList.end() );
			}

			// Insert class entry.
			void InsertEntry( ClassEntry * newEntry , const ClassID ancient )
			{
				TRACK_FUNCTION_SCOPE();

				// validate id
				if( ancient != INVALID_CLASS_ID )
					ValidateClassID( ancient );

				m_nameMap.insert( newEntry->m_className , newEntry );

				if( ancient == INVALID_CLASS_ID )
					// root class, skip it
					m_IObjectClass = newEntry;
				else
					// build inheritance table for searching compatibility
					BuildInheritance( newEntry , ancient );
			}

		public:

			// instance
			static _ObjectFactoryImpl& instance( void )
			{
				static _ObjectFactoryImpl s_instanceObjectFactoryImpl;
				return s_instanceObjectFactoryImpl;
			}

			// Register class.
			ClassID RegisterClass( const WString& name , const ClassID ancient , ConstructObjectFn constructor , size_t size )
			{
				TRACK_FUNCTION_SCOPE();

				// lock
				LockGuard<ThreadLock> s_registerClassCallLockGuard( m_registerLock );

				// see if entry exists
				if( m_nameMap.has_key( name ) )
				{
					TRACE_ERROR( _S("_ObjectFactoryImpl::RegisterClass: Class '%s' is registered.") , name.c_str() );
					FATAL_ABORT( _S("Duplicate class register") );
				}

				ClassEntry * newEntry = new ClassEntry(
					constructor == NULL ? CLASS_STATIC : CLASS_DYNAMIC , ancient ,
					name , size , constructor );
				InsertEntry( newEntry , ancient );

				if( constructor == NULL )
					TRACE_INFO( _S("Registered static class: %s.") , name.c_str() );
				else
					TRACE_INFO( _S("Registered dynamic class: %s.") , name.c_str() );

				return (ClassID)newEntry;
			}

			// Register Interface.
			ClassID RegisterInterface( const WString& name , const ClassID ancient )
			{
				TRACK_FUNCTION_SCOPE();

				// lock
				LockGuard<ThreadLock> s_registerClassCallLockGuard( m_registerLock );

				// see if entry exists
				if( m_nameMap.has_key( name ) )
				{
					TRACE_ERROR( _S("_ObjectFactoryImpl::RegisterInterface: Class '%s' is registered.") , name.c_str() );
					FATAL_ABORT( _S("Duplicate interface register") );
				}

				ClassEntry * newEntry = new ClassEntry( CLASS_INTERFACE , ancient , name , 0 );
				InsertEntry( newEntry , ancient );
				return (ClassID)newEntry;
			}

			// Create Object by Name.
			IObject * CreateObject( const WString& name )
			{
				TRACK_FUNCTION_SCOPE();

				NameMap::iterator it = m_nameMap.find( name );
				if( it == m_nameMap.end() )
				{
					TRACE_ERROR( _S("_ObjectFactoryImpl::CreateObject: Class '%s' is not registered.") ,
						name.c_str() );
					return NULL;
				}

				ClassEntry * entry = it->second;

				switch( entry->m_classType )
				{
				case CLASS_INTERFACE:
					TRACE_ERROR( _S("_ObjectFactoryImpl::CreateObject: Interface '%s' cannot be instanced.") ,
						name.c_str() );
					return NULL;

				case CLASS_TYPE:
					TRACE_ERROR( _S("_ObjectFactoryImpl::CreateObject: Basic type '%s' cannot be created by factory.") ,
						name.c_str() );
					return NULL;

				case CLASS_STATIC:
					TRACE_ERROR( _S("_ObjectFactoryImpl::CreateObject: Class '%s' cannot be dynamically created.") ,
						name.c_str() );
					return NULL;
				
				case CLASS_DYNAMIC:
					break;
				}

				return entry->m_constructor( entry->Alloc() );
			}

			// Create Object by ID.
			IObject * CreateObject( const ClassID id )
			{
				TRACK_FUNCTION_SCOPE();

				// validate id
				ValidateClassID( id );

				ClassEntry * entry = (ClassEntry*)id;

				switch( entry->m_classType )
				{
				case CLASS_INTERFACE:
					TRACE_ERROR( _S("_ObjectFactoryImpl::CreateObject: Interface '%s' cannot be instanced.") ,
						id->m_className.c_str() );
					return NULL;

				case CLASS_TYPE:
					TRACE_ERROR( _S("_ObjectFactoryImpl::CreateObject: Basic type '%s' cannot be created by factory.") ,
						id->m_className.c_str() );
					return NULL;

				case CLASS_STATIC:
					TRACE_ERROR( _S("_ObjectFactoryImpl::CreateObject: Class '%s' cannot be dynamically created.") ,
						id->m_className.c_str() );
					return NULL;
				
				case CLASS_DYNAMIC:
					break;
				}

				return entry->m_constructor( entry->Alloc() );
			}

			// Allocate space for object.
			void * AllocateObject( size_t size , const ClassID id )
			{
				TRACK_FUNCTION_SCOPE();

				// validate id
				ValidateClassID( id );

				ClassEntry * entry = (ClassEntry*)id;

				// check size
				assert( size == entry->m_size );

				return entry->Alloc();
			}

			// Deallocate space released by object.
			void DeallocateObject( void * ptr , const ClassID id )
			{
				TRACK_FUNCTION_SCOPE();

				// validate id
				ValidateClassID( id );

				ClassEntry * entry = (ClassEntry*)id;
				entry->Dealloc( ptr );
			}

			// Special function for IBuffer registration.
			ClassID RegisterIBuffer( const WString& name , const ClassID ancient )
			{
				LockGuard<ThreadLock> s_registerClassCallLockGuard( m_registerLock );

				assert( m_IBufferClass == NULL );

				m_IBufferClass = new ClassEntry( CLASS_INTERFACE , ancient , _S("IBuffer") , 0 );
				InsertEntry( m_IBufferClass , m_IObjectClass );

				return m_IBufferClass;
			}

			void * AllocateBuffer( size_t size )
			{
				if( m_IBufferClass == NULL )
				{
					IBuffer::StaticClassID();
				}

				// allocate buffer
				return m_IBufferClass->AllocBuffer( size );
			}

			void DeallocateBuffer( void * ptr , size_t size )
			{
				assert( m_IBufferClass );

				// deallocate buffer
				m_IBufferClass->DeallocBuffer( ptr , size );
			}

			// determine if 2 class is compatible
			bool IsCompatible( const ClassID child , const ClassID ancient ) const
			{
				// validate id
				ValidateClassID( child );
				ValidateClassID( ancient );

				const ClassEntry * childEntry = ((const ClassEntry*)child);

				return std::binary_search( childEntry->m_ancientList.begin() ,
					childEntry->m_ancientList.end() , ancient );
			}

		private:

			// register lock
			ThreadLock			m_registerLock;

			typedef dict< WString , ClassEntry * > NameMap;
			NameMap				m_nameMap;

			// IObject is root
			ClassEntry *		m_IObjectClass;

			// IBuffer is outside any other classes
			ClassEntry *		m_IBufferClass;

		};
	}
	
	// interfaces

	// register class
	ClassID ObjectFactory::RegisterClass( const WString& name , const ClassID ancient , ConstructObjectFn constructor , size_t size )
	{
		return _ObjectFactoryImpl::instance().RegisterClass( name , ancient , constructor , size );
	}

	// register interface
	ClassID ObjectFactory::RegisterInterface( const WString& name , const ClassID ancient )
	{
		return _ObjectFactoryImpl::instance().RegisterInterface( name , ancient );
	}

	// create object from class
	IObject * ObjectFactory::CreateObject( const WString& name )
	{
		return _ObjectFactoryImpl::instance().CreateObject( name );
	}

	// create object from class
	IObject * ObjectFactory::CreateObject( const ClassID id )
	{
		return _ObjectFactoryImpl::instance().CreateObject( id );
	}

	// allocate new space for object
	void * ObjectFactory::AllocateObject( size_t size , const ClassID id )
	{
		return _ObjectFactoryImpl::instance().AllocateObject( size , id );
	}

	// deallocate object instance space
	void ObjectFactory::DeallocateObject( void * ptr , const ClassID id )
	{
		_ObjectFactoryImpl::instance().DeallocateObject( ptr , id );
	}

	// determine if 2 class is compatible
	bool ObjectFactory::IsCompatible( ClassID child , ClassID ancient )
	{
		return _ObjectFactoryImpl::instance().IsCompatible( child , ancient );
	}

	void * ObjectFactory::AllocateBuffer( size_t size )
	{
		return _ObjectFactoryImpl::instance().AllocateBuffer( size );
	}

	void ObjectFactory::DeallocateBuffer( void * ptr , size_t size )
	{
		return _ObjectFactoryImpl::instance().DeallocateBuffer( ptr , size );
	}

	ClassID ObjectFactory::RegisterIBuffer(
		const WString& name /* should be 'IBuffer' */ , const ClassID ancient /* should be IObject */ )
	{
		return _ObjectFactoryImpl::instance().RegisterIBuffer( name , ancient );
	}

	// Register class slot.
	bool RegisterClassSlot( ClassID id , const char * key , void * value )
	{
		return id->RegisterSlot( key , value );
	}

	// Delete class slot.
	bool DelClassSlot( ClassID id , const char * key )
	{
		return id->DeleteSlot( key );
	}

	// Get class slot.
	bool GetClassSlot( ClassID id , const char * key , void *& value )
	{
		return id->GetSlot( key , value );
	}

	// Set class slot.
	bool SetClassSlot( ClassID id , const char * key , void * value )
	{
		return id->SetSlot( key , value );
	}

	// Get slot from inheritance.
	bool GetInheritClassSlot( ClassID id , const char * key , void *& value )
	{
		return id->GetInheritSlot( key , value );
	}

} // namespace xeres
